package com.service.business.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.service.business.base.dto.LoginUserSession;
import com.service.business.base.enums.SmsTypeEnum;
import com.service.business.base.service.ConfigService;
import com.service.business.member.service.MemberJAService;
import com.service.business.member.service.MemberService;
import com.service.business.member.service.MemberStoreService;
import com.service.business.order.controller.rep.BaseOrderLockRep;
import com.service.business.order.controller.rep.MemberLockAssetsRep;
import com.service.business.order.controller.rep.OrderLockComboPageRep;
import com.service.business.order.controller.req.OrderComboPageReq;
import com.service.business.order.controller.req.OrderLockReq;
import com.service.business.order.service.OrderProductLockComboService;
import com.service.business.order.utils.OrderUtils;
import com.service.business.pay.controller.rep.PayTypeRep;
import com.service.business.pay.controller.req.PayOrderReq;
import com.service.business.pay.service.PayService;
import com.service.business.product.controller.rep.ProductPayTypeRep;
import com.service.business.product.service.ProductLockComboService;
import com.service.business.product.service.ProductPayTypeService;
import com.service.business.support.MemberStoreLockUtils;
import com.service.business.support.OrderLockUtils;
import com.service.business.support.SessionUtil;
import com.service.business.support.VerifyCodeUtil;
import com.service.business.utils.DateUtil;
import com.service.core.exception.CommonException;
import com.service.core.rest.response.CommonPageRep;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.*;
import com.service.dao.mapper.OrderProductLockComboMapper;
import com.service.dao.model.*;
import com.service.utility.common.MathUtil;
import com.service.utility.common.UUIDUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.redisson.RedissonRedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("orderProductLockComboService")
public class OrderProductLockComboServiceImpl extends ServiceImpl<OrderProductLockComboMapper, OrderProductLockCombo>
		implements OrderProductLockComboService {

	@Autowired
	private SessionUtil sessionUtil;
	@Autowired
	private OrderUtils orderUtils;
	@Autowired
	private MemberService memberService;
	@Autowired
	private ProductLockComboService productLockComboService;
	@Autowired
	private MemberStoreService memberStoreService;
	@Autowired
	private MemberStoreLockUtils memberStoreLockUtils;
	@Autowired
	private PayService payService;
	@Autowired
	private MemberJAService memberJAService;
	@Autowired
	private ProductPayTypeService productPayTypeService;
	@Autowired
	private ConfigService configService;
	@Autowired
	private VerifyCodeUtil verifyCodeUtil;
	@Autowired
	private OrderLockUtils orderLockUtils;

	@Value("${productLockComboOrderPrefix}")
	private String productLockComboOrderPrefix;

	private final BigDecimal baseNum = new BigDecimal(100L);

	@Override
	public CommonPageRep<OrderLockComboPageRep> page(OrderComboPageReq pageReq) {
		Page<OrderProductLockCombo> page = PageHelper.startPage(pageReq.getPageNo(), pageReq.getPageSize());
		baseMapper.listLockOrderByMemberParams(sessionUtil.getUser().getUserId(), pageReq.getOrderStatus(), pageReq.getCoins().getCode());
		Map<String, SysConfig> configMap = configService.getConfigMapByGroup(ConfigConstants.Group.mall.getCode());
		SysConfig config = configMap.get(ConfigConstants.ORDER_OVERDUE_TIME);
		List<OrderLockComboPageRep> pageRepList = page.getResult().stream().map(item -> {
			OrderLockComboPageRep pageRep = ModelMapperUtil.getStrictModelMapper().map(item, OrderLockComboPageRep.class);
			List<ProductPayTypeRep> list = productPayTypeService.listByOrderType(item.getOrderType());
			pageRep.setOrderMoneyCoin(list.get(0).getPayName());
			if (ObjectUtils.nullSafeEquals(pageReq.getOrderStatus(), HashRateOrderEnums.Status.not_pay.getCode())) {
				pageRep.setValidTime(DateUtil.getValidTime(item.getCreateTime(), Integer.parseInt(config.getConfValue())));
			}
			return pageRep;
		}).collect(Collectors.toList());

		return CommonPageRep.buildResultRep(page, pageRepList);
	}

	/**
	 * @Description: 用户锁仓资产统计
	 * @Author: ty
	 * @Date: 2021/5/11 11:09
	 * @param: []
	 * @return: com.service.business.order.controller.rep.MemberLockAssetsRep
	 **/
	@Override
	public MemberLockAssetsRep assetsLock(CoinsEnum coins) {
		Integer memberId = sessionUtil.getUser().getUserId();
		//统计当前用户锁仓数量
		BigDecimal currentLockNum = baseMapper.totalLockNumByMemberIdAndStatus(memberId, HashRateOrderEnums.Status.payed_effective.getCode(), coins.getCode());
		BigDecimal historyLockNum = baseMapper.totalLockNumByMemberIdAndStatus(memberId, null, coins.getCode());
		List<OrderProductLockCombo> lockComboList = baseMapper.listLockOrderByMemberParams(memberId, HashRateOrderEnums.Status.payed_effective.getCode(), coins.getCode());
		BigDecimal yesterdayIncome = BigDecimal.ZERO;
		if (!ObjectUtils.isEmpty(lockComboList)) {
			for(OrderProductLockCombo lockCombo :lockComboList){
				BigDecimal dailyIncome = lockCombo.getDailyInterestRate().multiply(lockCombo.getLockNum());
				yesterdayIncome = yesterdayIncome.add(dailyIncome);
			}
		}
		BigDecimal totalHistoryLockIncome = memberJAService.getSomedayTotalStatement(memberId,coins.getName(), null,MemberJAType.getLockType());
		MemberLockAssetsRep rep = MemberLockAssetsRep.builder()
				.currentLockNum(currentLockNum)
				.historyLockNum(historyLockNum)
				.yesterdayIncome(yesterdayIncome)
				.totalHistoryLockIncome(totalHistoryLockIncome)
				.build();
		return rep;
	}

	/**
	 * @Description: 存币业务下单
	 * @Author: ty
	 * @Date: 2021/5/10 15:49
	 * @param: [orderReq]
	 * @return: com.service.business.order.controller.rep.BaseOrderLockRep
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void placeLockOrder(Integer lockComboId, OrderLockReq lockReq) {
		LoginUserSession session = sessionUtil.getUser();

		if(session.getStoreFreezeStatus() == com.service.dao.enums.MemberEnums.StoreStatus.FROZEN.getCode()){
			throw new CommonException("账户资产已冻结，如有疑问请联系客服","account_assets_frozen");
		}

		ProductLockCombo combo = productLockComboService.getById(lockComboId);
		if (ObjectUtils.isEmpty(combo) || !ObjectUtils.nullSafeEquals(combo.getComboStatus(), CommonStatusEnums.ACTIVE.getCode())) {
			throw new CommonException("不存在此套餐", "hash_rate_combo_not_exist");
		}
		if(lockReq.getLockNum().intValue()%combo.getLockLimit().intValue() != 0){
			throw new CommonException("锁仓数量"+lockReq+"不是"+combo.getLockLimit()+"的整数倍", "hash_rate_combo_not_exist");
		}
		List<ProductPayTypeRep> productPayTypeList = productPayTypeService.listValidPayTypeByOrderType(lockReq.getLockNum(), sessionUtil.getUser().getUserId(), combo.getComboType());
		if (ObjectUtils.isEmpty(productPayTypeList)) {
			throw new CommonException("会员余额不足", "store_balance_not_enough");
		}
		//判断当前用户是否有该产品的未支付订单
		Integer notPayOrderNum = baseMapper.totalNotConfirmLockOrder(session.getUserId(), combo.getId());
		if (notPayOrderNum > 0) {
			throw new CommonException("您已有该套餐的未支付订单，请先支付", "already_have_not_pay_order");
		}
		Date currentDate = new Date();
		OrderProductLockCombo orderProductLockCombo = new OrderProductLockCombo();
		orderProductLockCombo.setOrderType(combo.getComboType());
		orderProductLockCombo.setLockNo(buildLockOrderNo());
		orderProductLockCombo.setLockDay(combo.getLockDay());
		orderProductLockCombo.setMemberId(session.getUserId());
		orderProductLockCombo.setLockNum(lockReq.getLockNum());
		orderProductLockCombo.setYearInterestRate(combo.getYearInterestRate());
		orderProductLockCombo.setCoinsId(combo.getCoinsId());
		orderProductLockCombo.setCoinsName(combo.getCoinsName());
		orderProductLockCombo.setOrderStatus(HashRateOrderEnums.Status.not_pay.getCode());
		orderProductLockCombo.setCreateTime(currentDate);
		BigDecimal dailyRate = MathUtil.div(MathUtil.div(combo.getYearInterestRate(), baseNum, 8), new BigDecimal(365), 8);
		BigDecimal totalIncome = MathUtil.mul(dailyRate, new BigDecimal(combo.getLockDay()));
		orderProductLockCombo.setTotalIncome(totalIncome);
		orderProductLockCombo.setOrderSource(HashRateOrderEnums.Source.LOCK_MINING_ORDER.getSourceCode());
		orderProductLockCombo.setLockStatus(LockProductEnums.LockStatus.NOT_EXPIRED.getCode());
		baseMapper.insert(orderProductLockCombo);
		verifyCodeUtil.verifyTextVerifyCode(session.getAccount(), lockReq.getVerifyCode(), SmsTypeEnum.PAY);
		memberService.validSafetyPwd(session.getUserId(), lockReq.getSafetyPwd());

		List<String> lockKey = Lists.newArrayList(orderLockUtils.buildLockOrderLockKey(orderProductLockCombo.getLockNo()));
		List<PayTypeRep> payTypeRepList = buildMemberStoreKeyAndPayTypeList(orderProductLockCombo.getOrderType(), lockReq.getPayGroup(), session, lockKey);
		RedissonRedLock redLock = orderLockUtils.getRedLock(lockKey);
		if (orderLockUtils.redLockTryLock(redLock)) {
			try {
				PayOrderReq payOrderReq = new PayOrderReq();
				payOrderReq.setSafetyPwd(lockReq.getSafetyPwd());
				payOrderReq.setPayGroup(lockReq.getPayGroup());
				payOrderReq.setVerifyCode(lockReq.getVerifyCode());
				payService.payProductLockComboOrder(orderProductLockCombo.getLockNo(), orderProductLockCombo, payTypeRepList, payOrderReq);
			} finally {
				redLock.unlock();
			}
		}
	}

	@Override
	public String buildLockOrderNo() {
		String no = productLockComboOrderPrefix + DateFormatUtils.format(new Date(), "yyyyMMdd") + UUIDUtil.randomUUID10().substring(0, 8);
		OrderProductLockCombo orderList = getOne(new QueryWrapper<OrderProductLockCombo>().lambda()
				.eq(OrderProductLockCombo::getLockNo, no).last(" limit 1"));
		if (!ObjectUtils.isEmpty(orderList)) {
			no = buildLockOrderNo();
		}
		return no;
	}

	@Override
	public BaseOrderLockRep getOrderPayInfo(String lockNo) {
		OrderProductLockCombo orderProductLockCombo = baseMapper.selectOne(new QueryWrapper<OrderProductLockCombo>().lambda()
				.eq(OrderProductLockCombo::getLockNo, lockNo)
				.eq(OrderProductLockCombo::getOrderStatus, HashRateOrderEnums.Status.not_pay.getCode())
		);
		List<ProductPayTypeRep> productPayTypeList = productPayTypeService.listValidPayTypeByOrderType(orderProductLockCombo.getLockNum(), sessionUtil.getUser().getUserId(), orderProductLockCombo.getOrderType());
		if (ObjectUtils.isEmpty(productPayTypeList)) {
			throw new CommonException("会员余额不足", "store_balance_not_enough");
		}
		return BaseOrderLockRep.buildProductLockCombo(orderProductLockCombo, orderUtils.getOvertime(orderProductLockCombo.getCreateTime()), productPayTypeList);
	}

	@Override
	public void cancelLockOrder(String lockNo) {
		OrderProductLockCombo orderProductLockCombo = baseMapper.selectOne(new QueryWrapper<OrderProductLockCombo>().lambda()
				.eq(OrderProductLockCombo::getLockNo, lockNo)
				.eq(OrderProductLockCombo::getOrderStatus, HashRateOrderEnums.Status.not_pay.getCode())
		);
		orderProductLockCombo.setOrderStatus(HashRateOrderEnums.Status.noneffective.getCode());
		baseMapper.updateById(orderProductLockCombo);
	}

	private List<PayTypeRep> buildMemberStoreKeyAndPayTypeList(Integer orderType, Integer payGroup, LoginUserSession session, List<String> lockKey) {
		List<PayTypeRep> payTypeRepList = productPayTypeService.listByOrderTypeAndPayGroup(orderType, payGroup);
		for (PayTypeRep payTypeRep : payTypeRepList) {
			MemberStore memberStore = memberStoreService.getMemberStore(session.getUserId(), payTypeRep.getCoinsName());
			lockKey.add(MemberStoreLockUtils.buildStoreKey(memberStore.getMemberId(), memberStore.getId()));
		}
		return payTypeRepList;
	}

}
